﻿using System;
using System.Xml.Serialization;

namespace Bambit.XmlRpc
{
	/// <summary>
	/// Represnts a fault, or failure, in a <see cref="MethodCall"/>.  Only found in 
	/// a <see cref="MethodResponse"/> object
	/// </summary>
	/// <remarks>A <see cref="Fault"/> has a singe <see cref="Value"/> object, which MUST
	/// be of a <see cref="RpcStruct"/> type, which in turn MUST have 2 members, one <see cref="Member.Name">named </see> "faultCode"
	/// whose <see cref="Member.Value"/> must be of type <see cref="ItemChoiceType.Int"/> (or <see cref="ItemChoiceType.I4"/>; the
	/// other member must be <see cref="Member.Name">named </see> "faultString" whose <see cref="Member.Value"/> must be of type <see cref="ItemChoiceType.String"/>
	/// </remarks>
	[Serializable]
	[XmlType]
	public class Fault
	{

		#region Class Data

		private Value _Value;

		#endregion

		/// <summary>
		/// Gets or sets the value of this Fault
		/// </summary>
		/// <value>The value of this <see cref="Fault"/>.</value>
		/// <remarks>The <see cref="Value"/> of a <see cref="Fault"/> MUST be of a <see cref="RpcStruct"/> type, 
		/// which in turn MUST have 2 members, one <see cref="Member.Name">named </see> "faultCode" whose 
		/// <see cref="Member.Value"/> must be of type <see cref="ItemChoiceType.Int"/> (or <see cref="ItemChoiceType.I4"/>; 
		/// the other member must be <see cref="Member.Name">named </see> "faultString" whose <see cref="Member.Value"/> 
		/// must be of type <see cref="ItemChoiceType.String"/>
		/// </remarks>
		/// <exception cref="InvalidCastException">This is cast if the Value assigned is NOT a struct OR does not contain
		/// the required number, names and types of <see cref="RpcStruct.Members"/></exception>
		[XmlElement("value")]
		public Value Value
		{
			get { return _Value; }
			set
			{
				//	Make sure our value is correct
				if(value.ItemType!=ItemChoiceType.Struct)
					throw new InvalidCastException("The supplied data object is not a struct.  A Fault must only have a struct as the value");
				RpcStruct rpcStruct = (RpcStruct) value;
				if(2!= rpcStruct.Members.Length)
					throw new InvalidCastException("The supplied struct object for the fault must contain only two members.");
				Value faultCode, faultString;
				if (rpcStruct.Members[0].Name=="faultCode")
				{
					faultCode = rpcStruct.Members[0].Value;
					if(rpcStruct.Members[1].Name!="faultString" )
						throw new InvalidCastException("The supplied struct object must contain a faultString node");
					faultString = rpcStruct.Members[1].Value;
				}
				else if(rpcStruct.Members[1].Name=="faultCode")
				{
					faultCode = rpcStruct.Members[1].Value;
					if(rpcStruct.Members[1].Name!="faultString" )
						throw new InvalidCastException("The supplied struct object must contain a faultString node");
					faultString = rpcStruct.Members[0].Value;
				}
				else
					throw  new InvalidCastException("The supplied struct object must contain a faultCode node");
				if(faultCode.ItemType!=ItemChoiceType.Int &&faultCode.ItemType!=ItemChoiceType.I4)
					throw new InvalidCastException("The supplied struct object fault code must be an integer");
				if(faultString.ItemType!=ItemChoiceType.String)
					throw new InvalidCastException("The supplied struct object fault string must be a string");
				_Value = value;
				
			}
		}

		/// <summary>
		/// Creates a new <see cref="Fault"/> object
		/// </summary>
		/// <param name="faultCode">The code of the fault.</param>
		/// <param name="faultString">The description of the fault.</param>
		/// <returns>A new fault object initialized to the corrrect values.</returns>
		public static Fault CreateFault(int faultCode, string faultString)
		{
			return new Fault(faultCode, faultString);
		}

		public Fault() : this(0, "")
		{
			
		}

		public Fault(int faultCode, string faultString)
		{
			RpcStruct rpcStruct = new RpcStruct();
			rpcStruct.Add(new Member { Name = "faultCode", Value = faultCode });
			rpcStruct.Add(new Member { Name = "faultString", Value = faultString });
			Value = rpcStruct;
		}


		public string FaultString 
		{
			get
			{
				return (string)((RpcStruct) Value)["faultString"].Value;
			}
			set
			{
				((RpcStruct) Value)["faultString"].Value = value;
			}
		}

		public int FaultCode
		{
			get
			{
				return (int)((RpcStruct)Value)["faultCode"].Value;
			}
			set
			{
				((RpcStruct)Value)["faultCode"].Value = value;
			}
		}

		/// <summary>
		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </returns>
		/// <remarks>Returns the <see cref="FaultString"/></remarks>
		public override string ToString()
		{
			return FaultString;
		}

	}
}
